﻿using System;
using System.Linq;
using QDT.Common;
using QDT.Core;
using QDT.Core.Assignments;
using QDT.Core.Deals;
using QDT.Core.Members;
using QDT.Core.Projects;
using QDT.Services.Members;
using QDT.Services.Projects;

namespace QDT.Services.Assignments
{
    public class AssignmentService : ServiceBase, IAssignmentService
    {

        private static readonly object _asnyLock = new object();

        public Credit Bid(int userid, int assignmentId, decimal money, bool isAuto)
        {
            lock (_asnyLock)
            {
                var user = DbProvider.D<User>().FirstOrDefault(x => x.ID == userid && x.IsEffect && !x.IsDelete);
                if (user == null) throw new Exception("用户不存在或者已经被删除");
                var assignment = DbProvider.D<Assignment>().FirstOrDefault(x => x.ID == assignmentId && x.IsEffect && !x.IsDelete);
                if (assignment == null) throw new Exception("项目不存在");
                if ((user.GroupID.Equals(6) && !assignment.IsOffline) || (!user.GroupID.Equals(6) && assignment.IsOffline)) throw new Exception("用户组没有权限");
                if(assignment.EndTime <= DateTime.Now) throw  new Exception("债权转让过期");

                DbProvider.Refresh(user.UserAccount);
                DbProvider.Refresh(assignment);

                return Bid(user, assignment, money, isAuto);
            }
        }

        public Credit Bid(User user, Assignment assignment, decimal money, bool isAuto)
        {
            ValidateBid(user, assignment, money);
            user.UserAccount.CurrentChargeMoney -= money;
            var sellerCredit = DbProvider.GetByID<Credit>(assignment.CreditId);
            var project = DbProvider.GetByID<Project>(sellerCredit.ProjectID);
            if (project.RepayStartTime > DateTime.Now) throw new Exception("项目尚未完成，不能转让债权");
            var strategy = new RepayStrategyFactory().GetRepayStrategy(project);
            if (!strategy.IsAssignment) throw new Exception("此类型的项目不支持债权转让");
            var assign = strategy.CreateAssignment(sellerCredit, money, assignment.FeeRate, assignment.DiscountRate);
            var payMoney = money - assign.TotalDiscount + assign.CurrentEarning;

            var credit = new Credit
            {
                ProjectID = assignment.ProjectId,
                UserID = user.ID,
                UserName = user.UserName,
                Money = money,
                BuyMoney = money,
                CreateTime = DateTime.Now,
                Type = InvestType.Assignment,
                Status = CreditStatus.Biding,
                AssignmentID = assignment.ID,
                LastRepayTime = sellerCredit.LastRepayTime,
                NextRepayTime = sellerCredit.NextRepayTime,
                TotalStage = sellerCredit.TotalStage,
                CurrentStage = sellerCredit.CurrentStage,
                InnerStatus = (int)CreditStatus.Repay
            };

            var scheme = strategy.GetCreditRepaymenScheme(credit);
            credit.WaitRepayEarning = scheme.WaitRepayInterest;
            credit.WaitRepayPrincipal = scheme.WaitRepayPrincipal;

            assignment.LoadMoney += money;

            sellerCredit.Money -= money;
            sellerCredit.WaitRepayPrincipal -= money;
            sellerCredit.WaitRepayEarning -= credit.WaitRepayEarning;
            sellerCredit.RepayEarning += assign.CurrentEarning;

            credit.RepayEarning -= assign.CurrentEarning;

            var seller = DbProvider.GetByID<User>(assignment.UserId);

            var sellerlog = seller.Transaction(UserTransLog.TradeType.SellCredit, payMoney - assign.Fee, user.UserName, "卖出编号为{0}的债权.".FormatWith(assignment.ID));
            DbProvider.Add(sellerlog);

            var buyerLog = user.Transaction(UserTransLog.TradeType.InvestmentToPay, -payMoney, seller.UserName, "投资了编号为{0}的债权转让项目".FormatWith(assignment.ID));
            DbProvider.Add(buyerLog);

            if (assignment.NeedMoney == 0)
            {
                assignment.Status = AssignmentStatus.Completed;
                sellerCredit.IsAssign = false;
            }

            var ce = new CreditExtension
            {
                Credit = credit,
                Discount = assign.TotalDiscount,
                PayEarning = assign.CurrentEarning
            };

            DbProvider.Add(credit);
            DbProvider.Add(ce);

            DbProvider.SaveChanges();

            return credit;
        }

        public Assignment CreateAssignment(Project project, Credit credit, decimal money, double feeRate, double bonusRate, bool isOffline)
        {
            var strategy = new RepayStrategyFactory().GetRepayStrategy(project);
            var assignment = strategy.CreateAssignment(credit, money, feeRate, bonusRate, isOffline);
            assignment.IsOffline = isOffline;
            credit.IsAssign = true;

            DbProvider.Add(assignment);
            DbProvider.SaveChanges();
            return assignment;
        }

        public Assignment GetAssignmentById(int id)
        {
            return DbProvider.GetByID<Assignment>(id);
        }

        public IPagedList<Assignment> GetAssignmentPaged(int? page, int? userId = null, int? status = null, int? creditId = null, int? projectId = null, int pageSize = 5)
        {
            AssignsCancel();
            var filter = PredicateExtensionses.True<Assignment>();
            if (status.HasValue) filter = filter.And(x => x.InnerStatus == status);
            if (userId.HasValue) filter = filter.And(x => x.UserId == userId);
            if (creditId.HasValue) filter = filter.And(x => x.CreditId == creditId);
            if (projectId.HasValue) filter = filter.And(x => x.ProjectId == projectId);

            var pageNumber = page ?? 1;

            var source = DbProvider.D<Assignment>().Where(filter.And(x=>!x.IsOffline)).OrderBy(x => x.InnerStatus)
                                   .ThenByDescending(x => x.ID);
            return source.ToPagedList(pageNumber - 1, pageSize);
        }

        public void AssignCancel(Assignment assignment)
        {
            if (assignment == null || assignment.NeedMoney == 0 || assignment.Status == AssignmentStatus.Cancel) return;
            
            assignment.InnerStatus = (int)AssignmentStatus.Cancel;
            var credit = DbProvider.GetByID<Credit>(assignment.CreditId);
            credit.IsAssign = false;
        }

        public void AssignsCancel()
        {
            var time = DateTime.Now.Date.AddDays(-2);
            var assigns =
                DbProvider.D<Assignment>()
                    .Where(x => x.InnerStatus == (int)AssignmentStatus.Progress && x.CreateTime < time && !x.IsOffline).ToList();
            if (assigns.Count.Equals(0)) return;
            foreach (var assign in assigns)
            {
                AssignCancel(assign);
            }
            DbProvider.SaveChanges();
        }

        private void ValidateBid(User user, Assignment assign, decimal money)
        {
            //投资金额不能是负数和0
            if (money <= 0) throw new Exception("投资金额不能是负数和0");
            //项目状态是否处于进行中
            if (assign.IsDelete || !assign.IsEffect || assign.Status != AssignmentStatus.Progress) throw new Exception("项目状态是否处于进行中");
            //项目可投余额
            if (assign.NeedMoney < money) throw new Exception("项目可投余额不足");
            //用户余额
            if (user.UserAccount.Money < money * assign.BuyAmount / assign.Money) throw new Exception("用户余额不足");
            //是否是有效的投资人
            if (!user.IsLender || user.IsDelete || !user.IsEffect) throw new Exception("是否是有效的投资人");
            //项目最低投资金额
            if (50 > money && assign.NeedMoney >= assign.MinLoanMoney) throw new Exception("最低投资金额不足");
            if (user.ID == assign.UserId) throw new Exception("不能购买本人提交的债权转让项目！");
        }

    }
}
